﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartRoute.Data;
using SmartRoute.Randomness;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SmartRoute.Analytics
{
    //Please note! Comparing chromosomes is useful. This really should be a functional data structure.
    //Methods like "void mutateId(...)" breaks the functional nature of this data structure.
    //Certain things like intelligent water drops, local search, tabu search etc, will be made much harder.
    
    [Serializable]
    class Chromosome : ICloneable
    {
            //holds the permutations of stops for this chromosome
            List<Stop> stops;

            public List<Stop> Stops
            {
                get { return stops; }
                set { stops = value; }
            }
            //bus ID at each stop
            List<int> busId;

            public List<int> BusId
            {
                get { return busId; }
                set { busId = value; }
            }

            //starting location
            Stop depot;

            public Stop Depot
            {
                get { return depot; }
                set { depot = value; }
            }

            //ending location
            Stop school;

            public Stop School
            {
                get { return school; }
                set { school = value; }
            }

            //number of possible buses
            int numBuses;

            public int NumBuses
            {
                get { return numBuses; }
                set { numBuses = value; }
            }
            bool fixedNumberBuses;

            //constructor/initializer for a new chromosome for this population
            public Chromosome(List<SmartRoute.Data.Stop> stopOrder, Stop depot, Stop school, RandomSource rand, bool fixedNumBuses, int numBuses)
            {
                
                //cloned list of the potential stops
                SmartRoute.Data.Stop[] stopsArray = new SmartRoute.Data.Stop[stopOrder.Count];
                //indicates the start and stop for all buses
                this.depot = depot;
                this.school = school;
                this.numBuses = numBuses;
                this.fixedNumberBuses = fixedNumBuses;
                //makes shallow copy of the potential sotps
                for(int i = 0; i < stopOrder.Count; i++)
                {
                    stopsArray[i] = Stop.DeepCopy(stopOrder[i]);
                }
                //stopOrder.CopyTo(stopsArray, 0); WRONG
                //shuffle the stops to create first permutation
                rand.shuffle(stopsArray);
                busId = new List<int>();
                //randomly choses bus IDs for each stop - WITHOUT fixedNumBuses
                if(fixedNumBuses != true){
                    for (int i = 0; i < stopsArray.Length; i++)
                    {
                        busId.Add(rand.fromTo(0,stopsArray.Length-1));
                    }
                }
                //If they require that there is a fixed number of buses part of the stop permutation
                //is set aside.
                else
                {
                    for(int i = 0; i < numBuses; i++)
                    {
                        busId.Add(i);
                    }
                    for(int i = numBuses; i < stopsArray.Length; i++)
                    {
                        busId.Add(rand.fromTo(0,numBuses));
                    }
                }
                this.Stops = stopsArray.ToList();
                

            }

            //method that mutates the permutation
            public void mutatePermutation(RandomSource rand, int mutateChance)
            {
                int willMutate = rand.fromTo(0, 99);

                //
                if (willMutate < mutateChance)
                {
                    //Swapping uses indices to work. A truly random swap on a linked list
                    // makes little sense.
                    SmartRoute.Data.Stop[] tmp = this.Stops.ToArray();

                    //Destructively swap elements in tmp
                    rand.randomSwap(tmp);

                    //Rebuild the original list
                    this.Stops = tmp.ToList();
                }
            }

            public void mutateId(RandomSource rand, int mutateChance)
            {

                int willMutate = rand.fromTo(0, 99);
                if (willMutate < mutateChance)
                {
                    int nextPos = 0;
                    if (this.fixedNumberBuses == false)
                    {
                        //WARNING! fromTo is inclusive. Is nextPos an index?
                        nextPos = rand.Next(0, this.Stops.Count-1);
                    }
                    else
                    {
                        //WARNING! fromTo is inclusive. Is nextPos an index?
                        nextPos = rand.Next(numBuses, this.Stops.Count-1);
                    }
                    //WARNING! fromTo is inclusive. Is nextPos an index?
                    int nextId = rand.Next(0, this.numBuses-1);
                    this.BusId[nextPos] = nextId;
                }
            }

            //clone method for IClonable interface
            public object Clone()
            {
                object returning = this.MemberwiseClone();
                return returning;
            }

            //method found on http://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically
            public static T DeepCopy<T>(T obj)
            {
                object result = null;

                using (var ms = new MemoryStream())
                {
                    var formatter = new BinaryFormatter();
                    formatter.Serialize(ms, obj);
                    ms.Position = 0;

                    result = (T)formatter.Deserialize(ms);
                    ms.Close();
                }

                return (T)result;
            }

        }
    }
    

